/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.is.nb.jobs;

import cef.command.Command;
import cef.pui.EventConstructionException;
import cef.pui.pob.Builder;
import coalescent.data.meta.MetaInf_K69Data;
import coalescent.data.meta.MetaInf_Mngr;
import coalescent.is.cef.command.RunSamplers_Cmd;
import coalescent.is.cef.command.listener.RunSamplers_PW;
import coalescent.is.cef.pui.RunSamplers_EvtImpl;
import coalescent.nb.jobs.properties.Propertys_Coa;
import com.google.common.base.Supplier;
import commons.nb.jobs.Job;
import commons.nb.jobs.properties.*;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import static coalescent.cef.pui.pob.Builders_Coa.of_MetaInf_Mngr_K69Data_File;

/**
 *
 *
 * @version version
 * @author Susanta Tewari
 * @history Created on 11/20/12
 * @since 1.4.0
 */
public class Prob_K69_IS_Job extends Job<RunSamplers_EvtImpl> {

    /** data builder property for K69Data */
    private final BuilderProperty k69data_bp;

    public Prob_K69_IS_Job() {

        setName("is-genetree-prob");
        setDisplayName("<b>Data Likelihood<b>");
        setShortDescription("Computes data likelihood using IS.");

        final String prefix = "coalescent.is.ISamplers"; //J-

        //<editor-fold desc="Data">
        final String data_pfx = "coalescent.builder.GeneTree";
        k69data_bp = Propertys_Coa.of_Builder_K69Data(data_pfx);
        addComboProperty(k69data_bp); //J+



        // data-theta w/ metaInf
        final Property<Double> theta_prop          = ThetaProp("coalescent.builder.K69");
        final Supplier<MetaInf_K69Data> metaInf_sp = new Supplier<MetaInf_K69Data>() {

            @Override
            public MetaInf_K69Data get() {

                final Builder data_b = k69data_bp.getValue();
                final MetaInf_Mngr<MetaInf_K69Data> mngr =
                    of_MetaInf_Mngr_K69Data_File(data_b.getProperties(), data_b.getPrefix(),
                                                 "coalescent.MetaInf").build();

                return mngr.hasInfo() ? mngr.load() : null;
            }
        };

        theta_prop.setCustomProperty("inplaceData", metaInf_sp);
        addComboProperty(new ComboPropertyImpl(theta_prop));


        // data-exactProb
        final Property<Double> exactProb_prop      = MyProps.exactProb(prefix);
        final PropertyChangeListener exactProb_pcl = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {

                Double theta = theta_prop.getValue();

                updateExactProb(theta, metaInf_sp, exactProb_prop);
            }
        };

        k69data_bp.setPropertyChangeListenerDelegate(exactProb_pcl);    // wiring to data
        theta_prop.addDependent(exactProb_prop);    // wiring to theta
        theta_prop.setPropertyChangeListenerDelegate(exactProb_pcl);
        scheduleAfterGUI(new Runnable() {

            @Override
            public void run() {
                updateExactProb(theta_prop.getValue(), metaInf_sp, exactProb_prop);
            }

        });    // exactProb for init theta
        addComboProperty(new ComboPropertyImpl(exactProb_prop));    // </editor-fold>


        // samplers, trackers & markers
        addComboProperty(MyProps.samplers(prefix));    // samplers w/ itr strategies
        addJobProperty(Props_IS.TRACKERS.all());    // trackers (mean, se, ess)
        addComboProperty(MyProps.markers(prefix));    // markers (color & sliding-window-size)


        // output listeners
        addCommandOutputListener(new RunSamplers_PW());
    }

    private static void updateExactProb(Double theta, Supplier<MetaInf_K69Data> metaInf_sp,
            Property<Double> exactProb_prop) {

        final MetaInf_K69Data metaInf = metaInf_sp.get();

        if (metaInf != null) {

            if (metaInf.contains(theta)) {
                exactProb_prop.setValue(metaInf.getProb(theta).doubleValue());
            } else {
                exactProb_prop.setValue(null);
            }
        }
    }

    @Override
    protected RunSamplers_EvtImpl createEvent() throws EventConstructionException {
        return new RunSamplers_EvtImpl(getProperties());
    }

    @Override
    public Class<RunSamplers_EvtImpl> getEventImplType() {
        return RunSamplers_EvtImpl.class;
    }

    @Override
    protected Command createCommand(final RunSamplers_EvtImpl event) {
        return new RunSamplers_Cmd(event);
    }

    @Override
    public boolean isCancellable() {
        return RunSamplers_Cmd.isCancellable();
    }

    @Override
    public String getOnlineHelpURL() {
        return "https://freecode4susant.atlassian.net/wiki/display/COALESCENT/What%60s+New+in+Coalescent-1.4.2";
    }

    private static Property<Double> ThetaProp(final String prefix) {

        final Property<Double> propImpl = Propertys.ofDouble();

        propImpl.setName("theta");
        propImpl.setDisplayName("theta");
        propImpl.setShortDescription("Mutation rate");
        propImpl.setPropertySet(PropertySets.DATA);


        // key & default value & PropertyEditor
        propImpl.setKey(prefix + ".theta");
        propImpl.setValue(1.0);
        propImpl.setPropEditorClass(ThetaExactProb_ExPE_IFactory.class);

        return propImpl;
    }

    /**
     * Method description
     *
     * @return description
     */
    static class MyProps {

        static ComboPropertyImpl samplers(String prefix) {

            final ComboPropertyImpl combo = new ComboPropertyImpl();

            combo.addProperty(Props_IS.allSamplers(prefix));
            combo.addComboProperty(Props_IS.IterationStrategies(prefix));

            return combo;
        }

        private static ComboPropertyImpl markers(String prefix) {

            final ComboPropertyImpl combo = new ComboPropertyImpl();

            combo.addProperty(Props_IS.color(prefix, true));
            combo.addProperty(slidingWindow(prefix));

            return combo;
        }

        private static Property<Double> exactProb(String prefix) {

            final Property<Double> prop = Propertys.ofDouble();


            // descriptions & display category
            prop.setName("exact-prob");
            prop.setDisplayName("Exact Probability, if Known");
            prop.setShortDescription("Exact probability is displayed on the graph.");
            prop.setPropertySet(PropertySets.DATA);
            prop.setKey(prefix + ".exactProb");

            return prop;
        }

        private static Property<Integer> slidingWindow(final String prefix) {

            final Property<Integer> propImpl = Propertys.ofInteger();


            // descriptions & display category
            propImpl.setName("is-window-size");
            propImpl.setDisplayName("IS Window Size");
            propImpl.setShortDescription("IS Window Size");
            propImpl.setPropertySet(PropertySets.PARAMS);
            propImpl.setKey(prefix + ".slidingWindowSize");
            propImpl.setValue(50);

            return propImpl;
        }

        private MyProps() {}
    }
}
